/******************************************************************************
 *      Mestrado em Ci�ncia da Computa��o - UFMS
 *		Redes de Computadores - Prof. Dr. Ronaldo Alves Ferreira
 *		Alunos: Geraldo Landre, Marcel Tolentino, Vin�cius Bueno
 *****************************************************************************/

#include "server.h"

/*Variavel Condicional e Mutex */
pthread_mutex_t count_mutex   = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  condition_var = PTHREAD_COND_INITIALIZER;


/*
	#########################################################################
	############## Implementa��o das fun��es
	#########################################################################
*/

/* implementa��o do srv_getboundsockfd */
int srv_getboundsockfd(struct addrinfo *srv,vstring *port,int lg_reuseaddr)
{
    struct addrinfo hints;
    int sockfd,status;

    /* preencher o hints */
    memset(&hints,0,sizeof(struct addrinfo));

    /* preencher com as informa��es sobre o servidor local */
    hints.ai_family = AF_UNSPEC;		/* n�o importa, poderia ser AF_INET (IP), ou AF_INET6 (IPv6), ... */
                                            /* usar AF_UNSPEC aumenta a compatibilidade com IPv6 (e outros) */
    hints.ai_socktype = SOCK_STREAM;	/* TCP stream sockets */
    hints.ai_flags = AI_PASSIVE;		/* diz para que o SO atribua o meu endere�o local */

    /* getaddrinfo */
    status = getaddrinfo(NULL,port->pt,&hints,&srv);
    if( status != 0 )
        fprintf(stderr, "erro srv_getaddrinfo: %s\n", gai_strerror(status));

    printf ("srv_getaddrinfo status: %d\n", status);

    /* sock_open */
    printf("chamada do sock_open no gethttosock\n");
    sockfd = sock_open(srv);

    /* Caso a porta j� esteja em uso, solicita o reuso da mesma, */
    /* para evitar a mensagem de erro: "Address already in use" */
    if(lg_reuseaddr)
    {
        fprintf(stdout, "[srv_bind]: reuseaddr=true\n");
        sock_reuseaddr(sockfd);
    }
    else
        fprintf(stdout, "[srv_bind]: reuseaddr=false\n");

    /* efetua o bind */
#ifdef MY_SOCKET_H
    status = my_bind(sockfd, srv->ai_addr, srv->ai_addrlen);
#else
    status = bind(sockfd, srv->ai_addr, srv->ai_addrlen);
#endif
    
    if( status == -1 )
    {
        perror("erro bind");
        exit(EXIT_FAILURE);
    }

    return sockfd;
}

int srv_listen(int sockfd,int backlog)
{
    /* iniciar o listen */
#ifdef MY_SOCKET_H
    int status = my_listen(sockfd, backlog);
#else
    int status = listen(sockfd, backlog);
#endif
    if(status == -1)
    {
        fprintf(stderr, "erro ao efetuar o listen!\n");
        exit(EXIT_FAILURE);
    }
    fprintf(stdout, "[Listen efetivado!]\n");

    return status;
}

int recvtimeout(int s,vstring *buf,int timeout)
{
    fd_set fds;
    int n;
    struct timeval tv;

    // set up the file descriptor set
    FD_ZERO(&fds);
    FD_SET(s, &fds);

    // set up the struct timeval for the timeout
    tv.tv_sec = timeout;
    tv.tv_usec = 0;

    // wait until timeout or data received
    n = select(s+1, &fds, NULL, NULL, &tv);
    if (n == 0) return -2; // timeout!
    if (n == -1) return -1; // error
    // data must be here, so do a normal recv()
#ifdef MY_SOCKET_H
    buf->length = my_recv(s,buf->pt,buf->mem_size,0);
#else
    buf->length = recv(s,buf->pt,buf->mem_size,0);
#endif
    buf->pt[buf->length] = '\0';
    return buf->length;
}

int server_recv(int socket, header_data **hd)
{
    int last_irc,parsed;
    vstring *buf = NULL,*aux = NULL;

    while(!is_finished(*hd) && parsed)
    {
        alloc_string(&buf,BUFFER);
        last_irc = recvtimeout(socket,buf,TIMEOUT);
        if(last_irc > 0)
        {
            if(aux != NULL && aux->length)
                concat_vstring(&aux,buf);
            else
                copy_vstring(&aux,buf);

            if(!(*hd)->end && aux->length > 0)
                parsed = parse(aux,hd);
        }
        else
            if(last_irc != -1)
                parsed = 0;
            else
                (*hd)->error = ERR_BAD_REQUEST;
        free_string(&buf);
    }
    return last_irc;
}

/*
	 Dever� existir uma inst�ncia separada desta fun��o para cada conex�o. Ela trata toda a comunica��o
	 uma vez que uma conex�o tenha sido estabelecida.Utilizado tanto no servidor multi-processo quanto
	 no servidor mult-thread
 */
void connection_handle(int sock)
{
    vstring* resp = NULL;
    int iRc = 0;
    int iSc = 0;
    header_data *header = new_header();

    do
    {
        // recebe...
        iRc = server_recv(sock,&header);
        if(iRc == -2 && !header->end)
        {
            if(!configure.threads)
                close_cache();

	    printf("Timeout!! Conexão Fechada!\n");
	    break;
	}
        if(iRc == -2)
            iRc = 1;
        //print_header_data(header);

        if(header->error)
            resp = get_msg_error(&header, NULL);
        else
            if(iRc != -1)
            {
                if(handle_request(&resp,&header) <= 0)
                    resp = get_msg_error(&header, NULL);
                //printf("saiu do handle request[persistent]\n");
            }
        printf("-------------------------------------------------------\n");
        printf("Nossa resposta tem tamanho %i:\n",resp->length);
        printf("-------------------------------------------------------\n");
#ifdef MY_SOCKET_H
        iSc = my_send(sock,resp->pt,resp->length,0);
#else
        iSc = send(sock,resp->pt,resp->length,0);
#endif

        if(iSc == -1)
            perror("erro no send");
        reset_header(&header);

    }while(header->alive && iRc > 0);

    shutdown(sock,SHUT_RDWR);
    free_string(&resp);
    // TODO fun��o pra dar free em tudo da header_data
}